'use strict';

const BaseService = require('./base');
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const url = require('url');
const qiniu = require('qiniu');
const common = require('../const/common');
const OSS = require('ali-oss');
const COS = require('cos-nodejs-sdk-v5');
const UPYUN = require('upyun');
const dayjs = require('dayjs');
const utils = require('../utils/utils');

class PicService extends BaseService {
  async setConfig(body) {
    let res = {};
    let params = {};
    params = body;
    await this.service.storage.saveConfig(params);

    return res;
  }
  async getConfig(pid) {
    const result = await this.service.storage.getConfig(pid);
    const data = result ? result : null;
    return data;
  }

  async getFileList(pid, page, perPage, content = '') {
    let list = await this.service.storage.getFileList(pid, page, perPage, content);
    for (let itemKey in list) {
      let item = list[itemKey];
      let uploadFileData = _.cloneDeep(item);
      let newItem = await this.customDomain(pid, uploadFileData);
      list[itemKey] = newItem;
    }

    return list;
  }

  async customDomain(pid, uploadFileData) {
    const config = await this.getConfig(pid);
    let originUrl = uploadFileData.baseUrl;
    let newUrl = null;
    switch (pid) {
      case common.smms :
        break;
      case common.github :
        newUrl = this.transJsdeliver(originUrl, config);
        break;
      case common.aliyunoss :
        newUrl = this.transUrlAliyunoss(originUrl, config);
        break;  
      case common.txcos :
        newUrl = this.transUrlTxcos(originUrl, config);
        break;    
      case common.qiniu :
        newUrl = this.transUrlQiniu(originUrl, config);
        break;
      case common.upyun :
        break;
    }
    if (newUrl) {
      uploadFileData.baseUrl = newUrl;
      for (let oneKey in uploadFileData.formatUrls) {
        let one = uploadFileData.formatUrls[oneKey];
        one.content = _.replace(one.content, originUrl, newUrl);
      }
    }

    return uploadFileData;
  }

  transJsdeliver(originUrl, config) {
    let newurl = null;
    if (_.isEmpty(config.domain)) {
      return newurl;
    }
    if (config.domain === 'cdn.jsdelivr.net') {
      const index = originUrl.indexOf('master');
      const fileSubPath = originUrl.substring(index + 6);
      newurl = 'https://' + config.domain;
      newurl += '/gh/' + config.username + '/' + config.repo + fileSubPath;
    }

    return newurl;
  }

  transUrlQiniu(originUrl, config) {
    if (_.isEmpty(config.domain)) {
      return null;
    }
    let urlObj = url.parse(originUrl);
    let newurl = config.protocol + "//" + config.domain + urlObj.path;
    return newurl;
  }

  transUrlAliyunoss(originUrl, config) {
    if (_.isEmpty(config.domain)) {
      return null;
    }
    let urlObj = url.parse(originUrl);
    let newurl = config.protocol + "//" + config.domain + urlObj.path;
    return newurl;
  }

  transUrlTxcos(originUrl, config) {
    if (_.isEmpty(config.domain)) {
      return null;
    }
    let urlObj = url.parse(originUrl);
    let newurl = config.protocol + "//" + config.domain + urlObj.path;
    return newurl;
  }

  async delFile(pid, fileId) {
    const res = await this.service.storage.delFile(pid, fileId);

    return res;
  }

  async uploadFile(pid, file) {
    let res = {};
    const tmpDir = this.service.pic.getTmpDir();
    const fullFilePath = path.normalize(path.join(tmpDir, file.filename));
    switch (pid) {
      case common.smms :
        res = this.uploadFileToSMMS(file, fullFilePath);
        break;
      case common.github :
        res = this.uploadFileToGithub(file, fullFilePath);
        break;
      case common.aliyunoss :
        res = this.uploadFileToAliyunoss(file, fullFilePath);
        break;    
      case common.txcos :
        res = this.uploadFileToTxcos(file, fullFilePath);
        break;    
      case common.qiniu :
        res = this.uploadFileToQiniu(file, fullFilePath);
        break;
      case common.upyun :
        res = this.uploadFileToUpyun(file, fullFilePath);
        break;
    }

    return res;
  }

  async uploadFileToSMMS(file, fullFilePath) {
    let result = {
      code: 0,
      msg: '',
      data: null
    };

    // check config
    const config = await this.getConfig(common.smms);
    if (_.isEmpty(config.secret_token)) {
      result.code = 1000;
      result.msg = '请先配置SM.MS图床token';
      return result;
    }
    const fileStream = fs.createReadStream(fullFilePath);
    try {
      //throw new Error('Sync Error');
      const headersObj = {
        'Content-Type': 'multipart/form-data',
        'Authorization': config.secret_token
      };
      const url = 'https://sm.ms/api/v2/upload';
      const response = await this.app.curl(url, {
        method: 'POST',
        headers: headersObj,
        files: {
          smfile: fileStream,
        },
        dataType: 'json',
        timeout: 600000,
      });
      let smmsResult = response.data;
      if (this.app.config.env === 'local') {
        this.app.logger.info('[PicService] [uploadFileToSMMS]: info result:%j', smmsResult);
      }
      if (smmsResult.success !== true) {
        this.app.logger.error('[PicService] [uploadFileToSMMS]: res error result:%j', smmsResult);
        let msg = _.get(MSGTEXT, [smmsResult.code], smmsResult.message);
        result.code = 1001;
        result.msg = `原因：${msg}`;
        return result;
      }

      // 结果转换
      const originalName = file.filename;
      const baseUrl = _.get(smmsResult, ['data', 'url'], '');
      const formatResult = this.formatImageUrls(originalName, baseUrl);
      const fileStates = fs.statSync(fullFilePath);
      const fileType = this.getFileType(originalName);
      let data = {
        originalName: originalName,
        baseUrl: baseUrl,
        formatUrls: formatResult,
        ext: smmsResult.data,
        size: fileStates.size,
        type: fileType
      };
      // 记录本地
      this.service.storage.saveUploadFile(common.smms, data);
      result.data = data;
      return result;
    } catch (e) {
      this.app.logger.error('[PicService] [uploadFileToSMMS]:  ERROR ', e);
      result.code = 1001;
      result.msg = `SM.MS网络超时不稳定，请稍后重试`;
    }
    return result;
  }

  async uploadFileToGithub(file, fullFilePath) {
    let result = {
      code: 0,
      msg: '',
      data: null
    };
    //return result;
    // check config
    const config = await this.getConfig(common.github);
    if (_.isEmpty(config.username) || _.isEmpty(config.repo) || _.isEmpty(config.branch)
      || _.isEmpty(config.token)) {
      result.code = 1000;
      result.msg = '请先配置Github';
      return result;
    }
    let bitmap = fs.readFileSync(fullFilePath);
    let base64File = Buffer.from(bitmap).toString('base64');
    let body = {
      'branch': config.branch,
			'message': 'upload file',
			'content': base64File,
    }
    const githubUrl = 'https://api.github.com/repos/' 
      + config.username + '/' + config.repo + '/contents/' + config.path + '/' + file.filename;
    const headersObj = {
      'Content-Type': 'application/json; charset=utf-8',
      'Authorization': 'token ' + config.token
    };
    console.log('githubUrl:', githubUrl);
    try {
      //throw new Error('Sync Error');
      const response = await this.app.curl(githubUrl, {
        method: 'PUT',
        headers: headersObj,
        data: body,
        dataType: 'json',
        timeout: 600000,
      });
      let githubResult = response.data;
      if (this.app.config.env === 'local') {
        this.app.logger.info('[PicService] [uploadFileToGithub]: info result:%j', githubResult);
      }
      if (_.isEmpty(githubResult.content)) {
        this.app.logger.error('[PicService] [uploadFileToGithub]: res error result:%j', githubResult);
        let msg = _.get(MSGTEXT, [githubResult.message], githubResult.message);
        result.code = 1001;
        result.msg = `原因：${msg}`;
        return result;
      }

      // 结果转换
      const originalName = file.filename;
      const baseUrl = _.get(githubResult, ['content', 'download_url'], '');
      let formatResult = this.formatImageUrls(originalName, baseUrl);
      // github使用fast-git下载地址
      let urlObj = url.parse(baseUrl);
      const downloadUrl = "https://raw.fastgit.org" + urlObj.path;
      formatResult.downloadUrl.content = downloadUrl;
      const fileStates = fs.statSync(fullFilePath); 
      const fileType = this.getFileType(originalName);
      let data = {
        originalName: originalName,
        baseUrl: baseUrl,
        formatUrls: formatResult,
        ext: githubResult.content,
        size: fileStates.size,
        type: fileType
      };
      result.data = data;
      // 记录本地
      this.service.storage.saveUploadFile(common.github, data);
      // 自定义域名
      if (!_.isEmpty(config.domain)) {
        let uploadFileData = _.cloneDeep(data);
        uploadFileData = await this.customDomain(common.github, uploadFileData);
        result.data = uploadFileData;
      }
      return result;
    } catch (e) {
      this.app.logger.error('[PicService] [uploadFileToGithub]:  ERROR ', e);
      result.code = 1001;
      result.msg = `github网络超时不稳定，请稍后重试`;
    }
    return result;
  }

  async uploadFileToQiniu(file, fullFilePath) {
    const self = this;
    let result = {
      code: 0,
      msg: '',
      data: null
    };
    // check config
    const config = await this.getConfig(common.qiniu);
    if (_.isEmpty(config.AccessKey) || _.isEmpty(config.SecretKey) || _.isEmpty(config.bucket)
      || _.isEmpty(config.domain)) {
      result.code = 1000;
      result.msg = '请先配置七牛云';
      return result;
    }
    const mac = new qiniu.auth.digest.Mac(config.AccessKey, config.SecretKey);
    const options = {
      scope: config.bucket,
    };
    const putPolicy = new qiniu.rs.PutPolicy(options);
    const uploadToken = putPolicy.uploadToken(mac);
    const qiniuConfig = new qiniu.conf.Config();
    qiniuConfig.zone = qiniu.zone[config.zone];
    //qiniuConfig.useHttpsDomain = true;
    qiniuConfig.useCdnDomain = true;
    const formUploader = new qiniu.form_up.FormUploader(qiniuConfig);
    const putExtra = new qiniu.form_up.PutExtra();
    const readableStream = fs.createReadStream(fullFilePath);
    let key = file.filename;
    if (!_.isEmpty(config.path)) {
      key = config.path + '/' + key;
    }

    return new Promise((resolve, reject) => {
      formUploader.putStream(uploadToken, key, readableStream, putExtra, function(respErr,
        respBody, respInfo) {
        if (respErr) {
          self.app.logger.error('[PicService] [uploadFileToQiniu]:  ERROR ', respErr);
          result.code = 1003;
          result.msg = `七牛云网络超时不稳定，请稍后重试`;
          resolve(result);
        }
        if (respInfo.statusCode !== 200) {
          let errCode = String(respInfo.statusCode);
          let msg = _.get(MSGTEXT, [errCode], respInfo.statusMessage);
          result.code = 1001;
          result.msg = `原因：${msg}`;
          resolve(result);
          return
        }
        // 结果转换
        const originalName = file.filename;
        let baseUrl = config.protocol + "//" + config.domain + '/';
        if (!_.isEmpty(config.path)) {
          baseUrl += config.path + '/';
        }
        baseUrl += file.filename;
        const formatResult = self.formatImageUrls(originalName, baseUrl);
        const fileStates = fs.statSync(fullFilePath); 
        const fileType = self.getFileType(originalName);
        let data = {
          originalName: originalName,
          baseUrl: baseUrl,
          formatUrls: formatResult,
          ext: respBody,
          size: fileStates.size,
          type: fileType
        };
        // 记录本地
        self.service.storage.saveUploadFile(common.qiniu, data);
        result.data = data;
        resolve(result);
      });
    });
  }

  async uploadFileToAliyunoss(file, fullFilePath) {
    let result = {
      code: 0,
      msg: '',
      data: null
    };
    // check config
    const config = await this.getConfig(common.aliyunoss);
    if (_.isEmpty(config.AccessKeyId) || _.isEmpty(config.AccessKeySecret) || _.isEmpty(config.bucket)) {
      result.code = 1000;
      result.msg = '请先配置阿里云OSS';
      return result;
    }
    const ossOptions = {
      accessKeyId: config.AccessKeyId,
      accessKeySecret: config.AccessKeySecret,
      bucket: config.bucket,
      region: config.region
    };
    // if (!_.isEmpty(config.domain)) {
    //   ossOptions.endpoint = config.domain;
    //   ossOptions.cname = true
    // }
    if (!_.isEmpty(config.protocol)) {
      ossOptions.secure = config.protocol === 'https:' ? true : false;
    }
    let pathFileName = file.filename;
    if (!_.isEmpty(config.path)) {
      pathFileName = config.path + '/' + pathFileName;
    }
    let client = new OSS(ossOptions);
    try {
      const fileStream = fs.createReadStream(fullFilePath);
      let aliyunossResult = await client.putStream(pathFileName, fileStream);

      if (this.app.config.env === 'local') {
        this.app.logger.info('[PicService] [uploadFileToAliyunoss]: info result:%j', aliyunossResult);
      }
      if (_.isEmpty(aliyunossResult.url)) {
        this.app.logger.error('[PicService] [uploadFileToAliyunoss]: res error result:%j', aliyunossResult);
        let errCode = String(respInfo.statusCode);
        let msg = _.get(MSGTEXT, [errCode], respInfo.statusMessage);
        result.code = 1001;
        result.msg = `原因：${msg}`;
        return result;
      }

      // 结果转换
      const originalName = file.filename;
      let baseUrl = _.get(aliyunossResult, ['url'], '');
      //baseUrl = _.replace(baseUrl, "http://", config.protocol);
      const formatResult = this.formatImageUrls(originalName, baseUrl);
      const fileStates = fs.statSync(fullFilePath); 
      const fileType = this.getFileType(originalName);
      let data = {
        originalName: originalName,
        baseUrl: baseUrl,
        formatUrls: formatResult,
        ext: aliyunossResult.res,
        size: fileStates.size,
        type: fileType
      };
      result.data = data;
      // 记录本地
      await this.service.storage.saveUploadFile(common.aliyunoss, data);
      // 自定义域名
      if (!_.isEmpty(config.domain)) {
        let uploadFileData = _.cloneDeep(data);
        uploadFileData = await this.customDomain(common.aliyunoss, uploadFileData);
        result.data = uploadFileData;
      }
      
      return result;
    } catch (e) {
      this.app.logger.error('[PicService] [uploadFileToAliyunoss]:  ERROR ', e);
      let errCode = String(e.code);
      let msg = _.get(MSGTEXT, [errCode], e.name);
      result.code = 1001;
      result.msg = `原因：${msg}`;
    }
    return result;
  }

  async uploadFileToTxcos(file, fullFilePath) {
    const self = this;
    let result = {
      code: 0,
      msg: '',
      data: null
    };
    // check config
    const config = await this.getConfig(common.txcos);
    if (_.isEmpty(config.SecretId) || _.isEmpty(config.SecretKey) || _.isEmpty(config.Bucket)) {
      result.code = 1000;
      result.msg = '请先配置腾讯云COS';
      return result;
    }
    const cosOptions = {
      SecretId: config.SecretId,
      SecretKey: config.SecretKey,
      Protocol: config.protocol
    };
    // if (!_.isEmpty(config.domain)) {
    //   cosOptions.Domain = config.domain;
    //   cosOptions.StrictSsl = config.protocol === 'https:' ? true : false;
    // }

    let pathFileName = file.filename;
    if (!_.isEmpty(config.path)) {
      pathFileName = config.path + '/' + pathFileName;
    }
    const fileStream = fs.createReadStream(fullFilePath);
    let client = new COS(cosOptions);
    let optingsObj = {
      Bucket: config.Bucket,
      Region: config.Region,
      Key: pathFileName,
      StorageClass: 'STANDARD',
      Body: fileStream,
    }
    return new Promise((resolve, reject) => {
      client.putObject(optingsObj, async function(err, respInfo) {
        if (err) {
          self.app.logger.error('[PicService] [uploadFileToTxcos]:  ERROR ', err);
          let msg = _.get(MSGTEXT, [err.code], '请查看日志');
          result.code = 1005;
          result.msg = `COS错误：${msg}`;
          resolve(result);
          return
        }  
        if (respInfo.statusCode !== 200) {
          result.code = 1001;
          result.msg = `原因：请查看日志[${respInfo.statusCode}]`;
          resolve(result);
          return
        }
        // 结果转换
        const originalName = file.filename;
        let baseUrl = _.get(respInfo, ['Location'], '');
        baseUrl = config.protocol + "//" + baseUrl;
        const formatResult = self.formatImageUrls(originalName, baseUrl);
        const fileStates = fs.statSync(fullFilePath); 
        const fileType = self.getFileType(originalName);
        let data = {
          originalName: originalName,
          baseUrl: baseUrl,
          formatUrls: formatResult,
          ext: respInfo,
          size: fileStates.size,
          type: fileType
        };
        result.data = data;
        // 记录本地
        await self.service.storage.saveUploadFile(common.txcos, data);
        // 自定义域名
        if (!_.isEmpty(config.domain)) {
          let uploadFileData = _.cloneDeep(data);
          uploadFileData = await self.customDomain(common.txcos, uploadFileData);
          result.data = uploadFileData;
        }
        resolve(result);
      });
    });
  }

  async uploadFileToUpyun(file, fullFilePath) {
    let result = {
      code: 0,
      msg: '',
      data: null
    };

    // check config
    const config = await this.getConfig(common.upyun);
    if (_.isEmpty(config.operator) || _.isEmpty(config.pwd) || _.isEmpty(config.Bucket)) {
      result.code = 1000;
      result.msg = '请先配置又拍云';
      return result;
    }
    let pathFileName = file.filename;
    if (!_.isEmpty(config.path)) {
      pathFileName = config.path + '/' + pathFileName;
    }
    const service = new UPYUN.Service(config.Bucket, config.operator, config.pwd);
    const client = new UPYUN.Client(service);
    try {
      const fileStream = fs.createReadStream(fullFilePath);
      let upyunResult = await client.putFile(pathFileName, fileStream);

      if (this.app.config.env === 'local') {
        this.app.logger.info('[PicService] [uploadFileToUpyun]: info result:%j', upyunResult);
      }

      // 结果转换
      const originalName = file.filename;
      const baseUrl = config.protocol + "//" + config.domain + '/' + pathFileName;
      const formatResult = this.formatImageUrls(originalName, baseUrl);
      const fileStates = fs.statSync(fullFilePath); 
      const fileType = this.getFileType(originalName);
      let data = {
        originalName: originalName,
        baseUrl: baseUrl,
        formatUrls: formatResult,
        ext: upyunResult,
        size: fileStates.size,
        type: fileType
      };
      result.data = data;
      // 记录本地
      await this.service.storage.saveUploadFile(common.upyun, data);
      
      return result;
    } catch (e) {
      this.app.logger.error('[PicService] [uploadFileToUpyun]:  ERROR ', e);
      const errCode = String(e.code);
      let msg = _.get(MSGTEXT, [errCode], '请查看日志');
      result.code = 1004;
      result.msg = `异常：${msg}`;
    }
    return result;
  }

  formatImageUrls(originalName, baseUrl) {
    if (_.isEmpty(originalName) || _.isEmpty(baseUrl)) {
      return null;
    }
    const result = {
      'imageUrl' : {
        title: '访问地址',
        content: `${baseUrl}`
      },
      'downloadUrl' : {
        title: '下载地址',
        content: `${baseUrl}`
      },
      'markdown' : {
        title: 'Markdown',
        content: `![${originalName}](${baseUrl})`
      },
      'html' : {
        title: 'HTML',
        content: `<img src="${baseUrl}" />`
      },
      'bbcode' : {
        title: 'BBCode',
        content: `[url=${baseUrl}][img]${baseUrl}[/img][/url]`
      }
    };

    return result;
  }

  async exportData() {
    const self = this;
    const storageDir = this.service.storage.getStorageDir();
    const backupDir = this.getBackupDir();
    const now = dayjs();
    const timeStr = now.format('YYYYMMDD-HHmmss');
    const distFileName = 'BakData_' + timeStr + '.json';
    const sourceFile = storageDir + 'db.json';
    const distFile = backupDir + distFileName;
    fs.copyFileSync(sourceFile, distFile);

    // open dir
    setTimeout(function(){
      self.ipcCall('pic.openDir', backupDir);
    }, 2000);

    return true;
  }

  async importData(tmpFilePath) {
    let rawdata = fs.readFileSync(tmpFilePath);
    let fileData = JSON.parse(rawdata);
    await this.service.storage.importData(fileData);

    return true;
  }

  /*
   * getTmpDir
   */
  getTmpDir() {
    const storageDir = this.service.storage.getStorageDir();
    const tmpDir = storageDir + 'tmp/';
    if (!fs.existsSync(tmpDir)) {
      utils.mkdir(tmpDir);
      utils.chmodPath(tmpDir, '777');
    }

    return tmpDir;
  }

  /*
   * getBackupDir
   */
  getBackupDir() {
    const storageDir = this.service.storage.getStorageDir();
    const backupDir = path.normalize(storageDir + 'backupData/');
    if (!fs.existsSync(backupDir)) {
      utils.mkdir(backupDir);
      utils.chmodPath(backupDir, '777');
    }

    return backupDir;
  }

  getFileType (filename) {
    let type = 'other';
    const fileExtName = path.extname(filename);
    console.log('fileExtName', fileExtName);
    if (_.includes([".bmp",".jpg",".png",".tif",".gif",".pcx",".tga",".exif",".fpx",".svg",".psd",".cdr",".pcd",".dxf",".ufo",".eps",".ai",".raw",".WMF",".webp",".avif"], fileExtName)) {
      type = 'image';
    } else if (_.includes([".wav",".flac",".ape",".alac",".mp3",".aac",".ogg",".vorbis",".opus"], fileExtName)) {
      type = 'music';
    } else if (_.includes([".wmv",".asf",".asx",".rm",".rmvb",".mp4",".3gp",".mov",".m4v",".avi",".dat",".mkv",".flv",".vob"], fileExtName)) {
      type = 'video';
    }

    return type;
  }
}

module.exports = PicService;